﻿using System;
using System.Collections.Generic;
using System.Linq;
using Adjunct.Framework.Nuspec;

namespace Adjunct.Framework.Dependencies
{
	public class DependencyInspector
	{
		public static DependencyNode BuildGraph(IEnumerable<NuspecManifest> manifests)
		{
			DependencyNode terminator = DependencyNode.NewTerminator();
			foreach (NuspecManifest manifest in manifests)
			{
				DependencyNode manifestNode = terminator.FindUsageOf(manifest.Metadata.Id) ?? terminator.AddUsage(manifest.Metadata.Id);
				foreach (NuspecDependency dependency in manifest.Metadata.Dependencies)
				{
					manifestNode.AddDependency(dependency.Id);
				}
			}
			return terminator;
		}

		public static void MergeTestsNodesWithSource(DependencyNode terminator)
		{
			HashSet<string> visited = new HashSet<string>();

			foreach (DependencyNode node in terminator.Traverse(n => n.UsedBy).Where(n => !visited.Contains(n.Id)))
			{
				if (node.Id.EndsWith(".Tests"))
				{
					string sourceId = node.Id.SafeTrimEnd(".Tests");
					DependencyNode sourcesNode = terminator.FindUsageOf(sourceId);
					if (sourcesNode == null)
					{
						throw new Exception("Source not found for Tests: " + node.Id);
					}
					node.MergeInto(sourcesNode);
				}
				visited.Add(node.Id);
			}
		}

		public static IEnumerable<DependencyNode> Walk(DependencyNode terminator)
		{
			Dictionary<string, int> counters = new Dictionary<string, int>();
			foreach (DependencyNode node in terminator.Traverse(n => n.UsedBy))
			{
				if (!counters.ContainsKey(node.Id))
				{
					counters.Add(node.Id, 0);
				}
				if (counters[node.Id] != node.DependsOnCount)
				{
					continue;
				}
				counters[node.Id]++;
				yield return node;
				foreach (DependencyNode usedBy in node.UsedBy)
				{
					if (!counters.ContainsKey(usedBy.Id))
					{
						counters.Add(usedBy.Id, 1);
					}
					else
					{
						counters[usedBy.Id]++;
					}
				}
			}
		}
	}
}